/*
 * The Sleuth Kit
 *
 * $Date: 2006/04/07 21:21:03 $
 *
 * Brian Carrier [carrier@sleuthkit.org]
 * Copyright (c) 2006 Brian Carrier, Basis Technology.  All rights reserved
 *
 * Copyright (c) 2003-2005 Brian Carrier.  All rights reserved
 *
 * DOS Partition Tables
 *
 *
 * This software is distributed under the Common Public License 1.0
 *
 */

#include "mm_tools.h"
#include "dos.h"


/* Check the extended partition flags */
#define dos_is_ext(x)	\
	((((x) == 0x05) || ((x) == 0x0F) || ((x) == 0x85)) ? 1 : 0)

/* 
 * dos_get_desc
 *
 * Return a buffer with a string description of the partition type
 * 
 * From: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html 
 */
static char *
dos_get_desc(uint8_t ptype)
{
#define DESC_LEN 64
    char *str = mymalloc(DESC_LEN);
    if (str == NULL)
	return "";

    switch (ptype) {
    case 0x00:
	snprintf(str, DESC_LEN, "Empty (0x00)");
	break;
    case 0x01:
	snprintf(str, DESC_LEN, "DOS FAT12 (0x01)");
	break;
    case 0x04:
    case 0x06:
	snprintf(str, DESC_LEN, "DOS FAT16 (0x%.2X)", ptype);
	break;
    case 0x05:
	snprintf(str, DESC_LEN, "DOS Extended (0x05)");
	break;
    case 0x07:
	snprintf(str, DESC_LEN, "NTFS (0x07)");
	break;
    case 0x08:
	snprintf(str, DESC_LEN, "AIX Boot (0x08)");
	break;
    case 0x09:
	snprintf(str, DESC_LEN, "AIX Data (0x09)");
	break;
    case 0x0b:
    case 0x0c:
	snprintf(str, DESC_LEN, "Win95 FAT32 (0x%.2X)", ptype);
	break;
    case 0x0e:
	snprintf(str, DESC_LEN, "Win95 FAT16 (0x0E)");
	break;
    case 0x0f:
	snprintf(str, DESC_LEN, "Win95 Extended (0x0F)");
	break;
    case 0x11:
	snprintf(str, DESC_LEN, "DOS FAT12 Hidden (0x11)");
	break;
    case 0x12:
	snprintf(str, DESC_LEN, "Hibernation (0x12)");
	break;
    case 0x14:
    case 0x16:
	snprintf(str, DESC_LEN, "DOS FAT16 Hidden (0x%.2X)", ptype);
	break;
    case 0x1b:
    case 0x1c:
	snprintf(str, DESC_LEN, "Win95 FAT32 Hidden (0x%.2X)", ptype);
	break;
    case 0x1e:
	snprintf(str, DESC_LEN, "Win95 FAT16 Hidden (0x1E)");
	break;
    case 0x42:
	snprintf(str, DESC_LEN, "Win LVM / Secure FS (0x42)");
	break;
    case 0x44:
	snprintf(str, DESC_LEN, "GoBack (0x44)");
	break;
    case 0x50:
    case 0x51:
    case 0x53:
    case 0x54:
	snprintf(str, DESC_LEN, "OnTrack Disk Manager (0x%.2X)", ptype);
	break;
    case 0x55:
	snprintf(str, DESC_LEN, "EZ-Drive (0x55)");
	break;
	return "EZ-Drive";
    case 0x57:
	snprintf(str, DESC_LEN, "DrivePro (0x57)");
	break;
    case 0x61:
	snprintf(str, DESC_LEN, "SpeedStor (0x61)");
	break;
    case 0x63:
	snprintf(str, DESC_LEN, "UNIX System V (0x63)");
	break;
    case 0x64:
    case 0x65:
    case 0x66:
    case 0x67:
    case 0x68:
    case 0x69:
	snprintf(str, DESC_LEN, "Novell Netware (0x%.2X)", ptype);
	break;
    case 0x82:
	snprintf(str, DESC_LEN, "Linux Swap / Solaris x86 (0x82)");
	break;
    case 0x83:
	snprintf(str, DESC_LEN, "Linux (0x83)");
	break;
    case 0x84:
	snprintf(str, DESC_LEN, "Hibernation (0x84)");
	break;
    case 0x85:
	snprintf(str, DESC_LEN, "Linux Extended (0x85)");
	break;
    case 0x86:
	snprintf(str, DESC_LEN, "NTFS Volume Set (0x86)");
	break;
    case 0x8e:
	snprintf(str, DESC_LEN, "Linux Logical Volume Manager (0x8e)");
	break;
    case 0x93:
	snprintf(str, DESC_LEN, "Linux Hidden (0x93)");
	break;
    case 0x9f:
	snprintf(str, DESC_LEN, "BSD/OS (0x9F)");
	break;
    case 0xa0:
    case 0xa1:
	snprintf(str, DESC_LEN, "Hibernation (0x%.2X)", ptype);
	break;
    case 0xa5:
	snprintf(str, DESC_LEN, "FreeBSD (0xA5)");
	break;
    case 0xa6:
	snprintf(str, DESC_LEN, "OpenBSD (0xA6)");
	break;
    case 0xa7:
	snprintf(str, DESC_LEN, "NextSTEP (0xA7)");
	break;
    case 0xa8:
	snprintf(str, DESC_LEN, "Mac OS X (0xA8)");
	break;
    case 0xa9:
	snprintf(str, DESC_LEN, "NetBSD (0xA9)");
	break;
    case 0xab:
	snprintf(str, DESC_LEN, "Mac OS X (0xAB)");
	break;
    case 0xb7:
	snprintf(str, DESC_LEN, "BSDI (0xB7)");
	break;
    case 0xb8:
	snprintf(str, DESC_LEN, "BSDI Swap (0xB8)");
	break;
    case 0xbe:
	snprintf(str, DESC_LEN, "Solaris 8 Boot (0xBE)");
	break;
    case 0xc2:
	snprintf(str, DESC_LEN, "Hidden Linux (0xC2)");
	break;
    case 0xc3:
	snprintf(str, DESC_LEN, "Hidden Linux Swap (0xC3)");
	break;
    case 0xc6:
    case 0xc7:
	snprintf(str, DESC_LEN,
	    "Corrupted Windows NT Volume / Stripe Set (0x%.2X)", ptype);
	break;
    case 0xee:
	snprintf(str, DESC_LEN, "GPT Safety Partition (0xEE)");
	break;
    case 0xfb:
	snprintf(str, DESC_LEN, "VMWare File System (0xFB)");
	break;
    case 0xfc:
	snprintf(str, DESC_LEN, "VMWare Swap (0xFC)");
	break;
	return "VMware Swap";
    case 0xfd:
	snprintf(str, DESC_LEN, "Linux RAID (0xFD)");
	break;
    case 0x02:
	snprintf(str, DESC_LEN, "XENIX root (0x02)");
	break;
    case 0x03:
	snprintf(str, DESC_LEN, "XENIX /usr (0x03)");
	break;
    case 0x0a:
	snprintf(str, DESC_LEN, "OS/2 Boot Manager (0x0a)");
	break;
	/*
	   case 0x0a:
	   snprintf(str, DESC_LEN, "Coherent swap (0x0a)");
	   break;
	   case 0x0a:
	   snprintf(str, DESC_LEN, "OPUS (0x0a)");
	   break;
	 */
    case 0x10:
	snprintf(str, DESC_LEN, "OPUS (0x10)");
	break;
    case 0x17:
	snprintf(str, DESC_LEN, "Hidden IFS/HPFS (0x17)");
	break;
    case 0x18:
	snprintf(str, DESC_LEN, "AST SmartSleep (0x18)");
	break;
    case 0x19:
    case 0x20:
    case 0x22:
    case 0x7e:
    case 0x7f:
    case 0xed:
    case 0xf7:
	snprintf(str, DESC_LEN, "Unused (0x%.2X)", ptype);
	break;
    case 0x21:
    case 0x23:
    case 0x26:
    case 0x31:
    case 0x33:
    case 0x34:
    case 0x36:
    case 0x71:
    case 0x73:
    case 0x76:
    case 0xf3:
	snprintf(str, DESC_LEN, "Reserved (0x%.2X)", ptype);
	break;
    case 0x24:
	snprintf(str, DESC_LEN, "NEC DOS 3.x (0x24)");
	break;
    case 0x32:
	snprintf(str, DESC_LEN, "NOS (0x32)");
	break;
    case 0x35:
	snprintf(str, DESC_LEN, "JFS on OS/2 or eCS  (0x35)");
	break;
    case 0x38:
	snprintf(str, DESC_LEN, "THEOS v3.2 2gb (0x38)");
	break;
	/*      
	   case 0x39:
	   snprintf(str, DESC_LEN, "Plan 9 (0x39)");
	   break;
	 */
    case 0x39:
	snprintf(str, DESC_LEN, "THEOS v4 Spanned (0x39)");
	break;
    case 0x3a:
	snprintf(str, DESC_LEN, "THEOS v4 4gb (0x3a)");
	break;
    case 0x3b:
	snprintf(str, DESC_LEN, "THEOS v4 Extended (0x3b)");
	break;
    case 0x3c:
	snprintf(str, DESC_LEN, "PartitionMagic Recovery (0x3c)");
	break;
    case 0x3d:
	snprintf(str, DESC_LEN, "Hidden NetWare (0x3d)");
	break;
    case 0x40:
	snprintf(str, DESC_LEN, "Venix 80286 (0x40)");
	break;
    case 0x41:
	snprintf(str, DESC_LEN,
	    "Linux/MINIX (Sharing Disk with DR-DOS) (0x41)");
	break;
	/*      
	   case 0x41:
	   snprintf(str, DESC_LEN, "Personal RISC Boot (0x41)");
	   break;
	   case 0x41:
	   snprintf(str, DESC_LEN, "PPC PReP Boot (0x41)");
	   break;
	 */
    case 0x43:
	snprintf(str, DESC_LEN,
	    "Linux Native (Sharing Disk with DR-DOS) (0x43)");
	break;
    case 0x45:
	snprintf(str, DESC_LEN, "Boot-US Boot Manager (0x45)");
	break;
	/*
	   case 0x45:
	   snprintf(str, DESC_LEN, "Priam (0x45)");
	   break;
	   case 0x45:
	   snprintf(str, DESC_LEN, "EUMEL/Elan  (0x45)");
	   break;
	 */
    case 0x46:
	snprintf(str, DESC_LEN, "EUMEL/Elan  (0x46)");
	break;
    case 0x47:
	snprintf(str, DESC_LEN, "EUMEL/Elan  (0x47)");
	break;
    case 0x48:
	snprintf(str, DESC_LEN, "EUMEL/Elan  (0x48)");
	break;
    case 0x4a:
	snprintf(str, DESC_LEN,
	    "Mark Aitchison's ALFS/THIN Lightweight Filesystem (0x4a)");
	break;
	/*case 0x4a:
	   snprintf(str, DESC_LEN, "AdaOS Aquila (0x4a)");
	   break; */
    case 0x4c:
	snprintf(str, DESC_LEN, "Oberon (0x4c)");
	break;
    case 0x4d:
    case 0x4e:
    case 0x4f:
	snprintf(str, DESC_LEN, "QNX 4.x (0x%.2X)", ptype);
	break;
	/*case 0x4f:
	   snprintf(str, DESC_LEN, "Oberon (0x4f)");
	   break; */
	/*case 0x52:
	   snprintf(str, DESC_LEN, "CP/M (0x52)");
	   break; */
    case 0x52:
	snprintf(str, DESC_LEN, "Microport SysV/AT (0x52)");
	break;
	/*case 0x56:
	   snprintf(str, DESC_LEN, "Golden Bow VFeature Partitioned Volume. (0x56)");
	   break; */
	/*case 0x56:
	   snprintf(str, DESC_LEN, "DM Converted to EZ-BIOS (0x56)");
	   break; */
    case 0x56:
	snprintf(str, DESC_LEN,
	    "AT&T MS-DOS 3.x Logically Sectored FAT. (0x56)");
	break;
    case 0x5c:
	snprintf(str, DESC_LEN, "Priam EDisk (0x5c)");
	break;
    case 0x70:
	snprintf(str, DESC_LEN, "DiskSecure Multi-Boot (0x70)");
	break;
    case 0x74:
	snprintf(str, DESC_LEN, "Scramdisk (0x74)");
	break;
    case 0x75:
	snprintf(str, DESC_LEN, "IBM PC/IX (0x75)");
	break;
	/*case 0x77:
	   snprintf(str, DESC_LEN, "M2FS/M2CS (0x77)");
	   break; */
    case 0x77:
	snprintf(str, DESC_LEN, "VNDI (0x77)");
	break;
    case 0x78:
	snprintf(str, DESC_LEN, "XOSL FS (0x78)");
	break;
    case 0x80:
	snprintf(str, DESC_LEN, "MINIX <=v1.4a (0x80)");
	break;
    case 0x81:
	snprintf(str, DESC_LEN, "MINIX >=v1.4b, Early Linux (0x81)");
	break;
	/*case 0x81:
	   snprintf(str, DESC_LEN, "Mitac Disk Manager (0x81)");
	   break; */
    case 0x87:
	snprintf(str, DESC_LEN, "NTFS Volume Set (0x87)");
	break;
    case 0x8a:
	snprintf(str, DESC_LEN, "Linux Kernel (0x8a)");
	break;
    case 0x8b:
	snprintf(str, DESC_LEN, "Legacy Fault Tolerant FAT32 (0x8b)");
	break;
    case 0x8c:
	snprintf(str, DESC_LEN,
	    "Legacy Fault Tolerant FAT32 using BIOS extd INT 13h (0x8c)");
	break;
    case 0x8d:
	snprintf(str, DESC_LEN,
	    "Free FDISK Hidden Primary DOS FAT12 (0x8d)");
	break;
    case 0x90:
	snprintf(str, DESC_LEN,
	    "Free FDISK Hidden Primary DOS FAT16 (0x90)");
	break;
    case 0x91:
	snprintf(str, DESC_LEN, "Free FDISK Hidden DOS Extended (0x91)");
	break;
    case 0x92:
	snprintf(str, DESC_LEN,
	    "Free FDISK Hidden Primary DOS Large FAT16 (0x92)");
	break;
    case 0x94:
	snprintf(str, DESC_LEN, "Amoeba Bad Block Table (0x94)");
	break;
    case 0x95:
	snprintf(str, DESC_LEN, "MIT EXOPC (0x95)");
	break;
    case 0x97:
	snprintf(str, DESC_LEN,
	    "Free FDISK Hidden Primary DOS FAT32 (0x97)");
	break;
    case 0x98:
	snprintf(str, DESC_LEN,
	    "Free FDISK Hidden Primary DOS FAT32 LBA (0x98)");
	break;
	/*case 0x98:
	   snprintf(str, DESC_LEN, "Datalight ROM-DOS Super-Boot (0x98)");
	   break; */
    case 0x99:
	snprintf(str, DESC_LEN, "DCE376 Logical Drive (0x99)");
	break;
    case 0x9a:
	snprintf(str, DESC_LEN,
	    "Free FDISK Hidden Primary DOS FAT16 LBA (0x9a)");
	break;
    case 0x9b:
	snprintf(str, DESC_LEN,
	    "Free FDISK Hidden DOS Extended LBA (0x9b)");
	break;
    case 0xa3:
	snprintf(str, DESC_LEN,
	    "HP Volume Expansion (SpeedStor Variant) (0xa3)");
	break;
    case 0xa4:
	snprintf(str, DESC_LEN,
	    "HP Volume Expansion (SpeedStor Variant) (0xa4)");
	break;
    case 0xaa:
	snprintf(str, DESC_LEN, "Olivetti Fat 12 1.44MB Service (0xaa)");
	break;
    case 0xae:
	snprintf(str, DESC_LEN, "ShagOS Filesystem (0xae)");
	break;
    case 0xaf:
	snprintf(str, DESC_LEN, "ShagOS swap (0xaf)");
	break;
    case 0xb0:
	snprintf(str, DESC_LEN, "BootStar Dummy (0xb0)");
	break;
    case 0xb1:
	snprintf(str, DESC_LEN,
	    "HP Volume Expansion (SpeedStor Variant) (0xb1)");
	break;
    case 0xb3:
	snprintf(str, DESC_LEN,
	    "HP Volume Expansion (SpeedStor Variant) (0xb3)");
	break;
    case 0xb4:
	snprintf(str, DESC_LEN,
	    "HP Volume Expansion (SpeedStor Variant) (0xb4)");
	break;
	/*case 0xb6:
	   snprintf(str, DESC_LEN, "HP Volume Expansion (SpeedStor Variant) (0xb6)");
	   break; */
    case 0xb6:
	snprintf(str, DESC_LEN,
	    "Corrupted Windows NT Mirror Set Master FAT16 (0xb6)");
	break;
    case 0xbb:
	snprintf(str, DESC_LEN, "Boot Wizard Hidden (0xbb)");
	break;
	/*case 0xc0:
	   snprintf(str, DESC_LEN, "CTOS (0xc0)");
	   break; */
	/*case 0xc0:
	   snprintf(str, DESC_LEN, "REAL/32 Secure Small (0xc0)");
	   break; */
	/*case 0xc0:
	   snprintf(str, DESC_LEN, "NTFT (0xc0)");
	   break; */
    case 0xc0:
	snprintf(str, DESC_LEN, "DR-DOS Secured (0xc0)");
	break;
    case 0xc1:
	snprintf(str, DESC_LEN, "DR-DOS Secured FAT12 (0xc1)");
	break;
    case 0xc4:
	snprintf(str, DESC_LEN, "DR-DOS Secured FAT16 <32M (0xc4)");
	break;
    case 0xc5:
	snprintf(str, DESC_LEN, "DR-DOS Secured Extended (0xc5)");
	break;
    case 0xc8:
	snprintf(str, DESC_LEN, "Reserved for DR-DOS 8.0+ (0xc8)");
	break;
    case 0xc9:
	snprintf(str, DESC_LEN, "Reserved for DR-DOS 8.0+ (0xc9)");
	break;
    case 0xca:
	snprintf(str, DESC_LEN, "Reserved for DR-DOS 8.0+ (0xca)");
	break;
    case 0xcb:
	snprintf(str, DESC_LEN, "DR-DOS 7.04+ Secured FAT32 CHS (0xcb)");
	break;
    case 0xcc:
	snprintf(str, DESC_LEN, "DR-DOS 7.04+ Secured FAT32 LBA (0xcc)");
	break;
    case 0xcd:
	snprintf(str, DESC_LEN, "CTOS Memdump? (0xcd)");
	break;
    case 0xce:
	snprintf(str, DESC_LEN, "DR-DOS 7.04+ FAT16X LBA (0xce)");
	break;
    case 0xcf:
	snprintf(str, DESC_LEN, "DR-DOS 7.04+ Secured EXT DOS LBA (0xcf)");
	break;
	/*case 0xd0:
	   snprintf(str, DESC_LEN, "REAL/32 Secure Big (0xd0)");
	   break; */
    case 0xd0:
	snprintf(str, DESC_LEN, "Multiuser DOS Secured (0xd0)");
	break;
    case 0xd1:
	snprintf(str, DESC_LEN, "Old Multiuser DOS Secured FAT12 (0xd1)");
	break;
    case 0xd4:
	snprintf(str, DESC_LEN,
	    "Old Multiuser DOS Secured FAT16 <32M (0xd4)");
	break;
    case 0xd5:
	snprintf(str, DESC_LEN,
	    "Old Multiuser DOS Secured extended (0xd5)");
	break;
    case 0xd6:
	snprintf(str, DESC_LEN,
	    "Old Multiuser DOS Secured FAT16 >=32M (0xd6)");
	break;
    case 0xd8:
	snprintf(str, DESC_LEN, "CP/M-86 (0xd8)");
	break;
    case 0xda:
	snprintf(str, DESC_LEN, "Non-FS Data (0xda)");
	break;
    case 0xdb:
	snprintf(str, DESC_LEN,
	    "Digital Research CP/M, Concurrent CP/M, Concurrent DOS (0xdb)");
	break;
	/*case 0xdb:
	   snprintf(str, DESC_LEN, "Unisys CTOS (0xdb)");
	   break; */
	/*case 0xdb:
	   snprintf(str, DESC_LEN, "KDG Telemetry SCPU boot (0xdb)");
	   break; */
    case 0xdd:
	snprintf(str, DESC_LEN, "Hidden CTOS Memdump?  (0xdd)");
	break;
    case 0xde:
	snprintf(str, DESC_LEN, "Dell Utilities FAT (0xde)");
	break;
	/*case 0xdf:
	   snprintf(str, DESC_LEN, "DG/UX Virtual Disk Manager (0xdf)");
	   break; */
	/*case 0xdf:
	   snprintf(str, DESC_LEN, "BootIt EMBRM (0xdf)");
	   break; */
    case 0xe0:
	snprintf(str, DESC_LEN,
	    "Reserved by STMicroelectronics for ST AVFS. (0xe0)");
	break;
    case 0xe1:
	snprintf(str, DESC_LEN,
	    "DOS Access or SpeedStor 12-bit FAT Extended (0xe1)");
	break;
    case 0xe3:
	snprintf(str, DESC_LEN, "DOS R/O or SpeedStor (0xe3)");
	break;
    case 0xe4:
	snprintf(str, DESC_LEN,
	    "SpeedStor 16-bit FAT Extended <1024 cyl. (0xe4)");
	break;
    case 0xe5:
	snprintf(str, DESC_LEN,
	    "Tandy MS-DOS with Logically Sectored FAT (0xe5)");
	break;
    case 0xe6:
	snprintf(str, DESC_LEN, "Storage Dimensions SpeedStor (0xe6)");
	break;
    case 0xeb:
	snprintf(str, DESC_LEN, "BeOS BFS (0xeb)");
	break;
    case 0xef:
	snprintf(str, DESC_LEN, "EFI File System (0xef)");
	break;
    case 0xf0:
	snprintf(str, DESC_LEN, "Linux/PA-RISC Boot Loader (0xf0)");
	break;
    case 0xf1:
	snprintf(str, DESC_LEN, "Storage Dimensions SpeedStor (0xf1)");
	break;
    case 0xf2:
	snprintf(str, DESC_LEN, "DOS 3.3+ Secondary (0xf2)");
	break;
    case 0xf4:
	snprintf(str, DESC_LEN, "SpeedStor Large (0xf4)");
	break;
	/*case 0xf4:
	   snprintf(str, DESC_LEN, "Prologue Single-Volume (0xf4)");
	   break; */
    case 0xf5:
	snprintf(str, DESC_LEN, "Prologue Multi-Volume (0xf5)");
	break;
    case 0xf6:
	snprintf(str, DESC_LEN, "Storage Dimensions SpeedStor (0xf6)");
	break;
    case 0xf9:
	snprintf(str, DESC_LEN, "pCache (0xf9)");
	break;
    case 0xfa:
	snprintf(str, DESC_LEN, "Bochs (0xfa)");
	break;
	/*case 0xfe:
	   snprintf(str, DESC_LEN, "SpeedStor >1024 cyl. (0xfe)");
	   break; */
	/*case 0xfe:
	   snprintf(str, DESC_LEN, "LANstep (0xfe)");
	   break; */
	/*case 0xfe:
	   snprintf(str, DESC_LEN, "IBM PS/2 IML (0xfe)");
	   break; */
    case 0xfe:
	snprintf(str, DESC_LEN,
	    "Windows NT Disk Administrator Hidden (0xfe)");
	break;
	/*case 0xfe:
	   snprintf(str, DESC_LEN, "Old Linux Logical Volume Manager (0xfe)");
	   break; */
    case 0xff:
	snprintf(str, DESC_LEN, "Xenix Bad Block Table (0xff)");
	break;
    default:
	snprintf(str, DESC_LEN, "Unknown Type (0x%.2X)", ptype);
	break;
    }

    return str;
}

/* 
 * Load an extended partition table into the structure in MM_INFO.
 *
 * sect_cur: The sector where the extended table is located
 * sect_ext_base: The sector of the primary extended table (this does
 *   not change for recursive calls)
 * table: a counter that identifies the table depth 
 *   (increases by 1 for each recursive call)
 * 
 * For the primary extended table, sect_cur == sect_ext_base 
 *
 * Return 1 on error and 0 on success
 *
 */
static uint8_t
dos_load_ext_table(MM_INFO * mm, DADDR_T sect_cur, DADDR_T sect_ext_base,
    int table)
{
    dos_sect sect;
    int i;
    char *table_str;
    SSIZE_T cnt;
    DADDR_T max_addr = (mm->img_info->size - mm->offset) / mm->block_size;	// max sector

    if (verbose)
	fprintf(stderr,
	    "dos_load_ext: Table Sector: %" PRIuDADDR
	    ", Primary Base Sector: %" PRIuDADDR "\n", sect_cur,
	    sect_ext_base);

    /* Read the partition table sector */
    cnt = mm_read_block_nobuf
	(mm, (char *) &sect, sizeof(sect), (DADDR_T) sect_cur);
    if (cnt != sizeof(sect)) {
	snprintf(tsk_errstr2, TSK_ERRSTR_L,
	    "Extended DOS table sector %" PRIuDADDR, sect_cur);
	if (cnt != -1) {
	    tsk_errno = TSK_ERR_MM_READ;
	    tsk_errstr[0] = '\0';
	}
	return 1;
    }

    /* Sanity Check */
    if (getu16(mm, sect.magic) != DOS_MAGIC) {
	tsk_errno = TSK_ERR_MM_MAGIC;
	snprintf(tsk_errstr, TSK_ERRSTR_L,
	    "Extended DOS partition table in sector %"
	    PRIuDADDR, sect_cur);
	tsk_errstr2[0] = '\0';
	return 1;
    }

    /* Add an entry of 1 length for the table  to the internal structure */
    if ((table_str = mymalloc(32)) == NULL)
	return 1;

    snprintf(table_str, 32, "Extended Table (#%d)", table);
    if (NULL == mm_part_add(mm, (DADDR_T) sect_cur, (DADDR_T) 1,
	    MM_TYPE_DESC, table_str, table, -1)) {
	return 1;
    }

    /* Cycle through the four partitions in the table 
     *
     * When another extended partition is found, it is processed
     * inside of the loop
     */
    for (i = 0; i < 4; i++) {
	dos_part *part = &sect.ptable[i];

	/* Get the starting sector and size, we currently
	 * ignore CHS */
	uint32_t part_start = getu32(mm, part->start_sec);
	uint32_t part_size = getu32(mm, part->size_sec);

	if (verbose)
	    fprintf(stderr,
		"load_ext: %d:%d    Start: %" PRIu32 "   Size: %"
		PRIu32 "  Type: %d\n", table, i, part_start, part_size,
		part->ptype);

	if (part_size == 0)
	    continue;

	/* partitions are addressed differently 
	 * in extended partitions */
	if (dos_is_ext(part->ptype)) {

	    /* part start is added to the start of the
	     * first extended partition (the primary
	     * extended partition) */

	    if (sect_ext_base + part_start > max_addr) {
		tsk_errno = TSK_ERR_MM_BLK_NUM;
		snprintf(tsk_errstr, TSK_ERRSTR_L,
		    "dos_load_ext_table: Starting sector too large for image");
		tsk_errstr2[0] = '\0';
		return 1;
	    }

	    if (NULL == mm_part_add(mm,
		    (DADDR_T) (sect_ext_base + part_start),
		    (DADDR_T) part_size, MM_TYPE_DESC,
		    dos_get_desc(part->ptype), table, i))
		return 1;


	    /* Process the extended partition */
	    if (dos_load_ext_table(mm, sect_ext_base + part_start,
		    sect_ext_base, table + 1))
		return 1;
	}

	else {
	    /* part_start is added to the start of the 
	     * current partition for the actual
	     * starting location */

	    if (sect_cur + part_start > max_addr) {
		tsk_errno = TSK_ERR_MM_BLK_NUM;
		snprintf(tsk_errstr, TSK_ERRSTR_L,
		    "dos_load_ext_table: Starting sector too large for image");
		tsk_errstr2[0] = '\0';
		return 1;
	    }
	    if (NULL == mm_part_add(mm, (DADDR_T) (sect_cur + part_start),
		    (DADDR_T) part_size, MM_TYPE_VOL,
		    dos_get_desc(part->ptype), table, i))
		return 1;
	}
    }

    return 0;
}


/* 
 * Load the primary partition table (MBR) into the internal
 * data structures in MM_INFO
 *
 * This will automatically call load_ext_table for extended
 * partitions
 *
 * sect_cur is the addres of the table to load
 *
 * 0 is returned if the load is successful and 1 if error
 */
static uint8_t
dos_load_prim_table(MM_INFO * mm, uint8_t test)
{
    dos_sect sect;
    int i;
    char *table_str;
    SSIZE_T cnt;
    DADDR_T taddr = mm->offset / mm->block_size + DOS_PART_SOFFSET;
    DADDR_T max_addr = (mm->img_info->size - mm->offset) / mm->block_size;	// max sector

    if (verbose)
	fprintf(stderr,
	    "dos_load_prim: Table Sector: %" PRIuDADDR "\n", taddr);

    /* Read the table */
    cnt = mm_read_block_nobuf
	(mm, (char *) &sect, sizeof(sect), DOS_PART_SOFFSET);

    if (cnt != sizeof(sect)) {
	snprintf(tsk_errstr2, TSK_ERRSTR_L,
	    "Primary DOS table sector %" PRIuDADDR, taddr);
	if (cnt != -1) {
	    tsk_errno = TSK_ERR_MM_READ;
	    tsk_errstr[0] = '\0';
	}
	return 1;
    }


    /* Sanity Check */
    if (mm_guessu16(mm, sect.magic, DOS_MAGIC)) {
	tsk_errno = TSK_ERR_MM_MAGIC;
	snprintf(tsk_errstr, TSK_ERRSTR_L,
	    "File is not a DOS partition (invalid primary magic) (Sector: %"
	    PRIuDADDR ")", taddr);
	tsk_errstr2[0] = '\0';
	return 1;
    }

    /* Because FAT and NTFS use the same magic - check for a
     * standard MS OEM name and sizes.  Not a great check, but we can't 
     * really test the table entries.  
     */
    if (test) {

	if (verbose)
	    fprintf(stderr,
		"dos_load_prim_table: Testing FAT/NTFS conditions\n");

	if (strncmp("MSDOS", sect.oemname, 5) == 0) {
	    tsk_errno = TSK_ERR_MM_MAGIC;
	    snprintf(tsk_errstr, TSK_ERRSTR_L,
		"dos_load_prim_table: MSDOS OEM name exists");
	    tsk_errstr2[0] = '\0';
	    return 1;
	}
	else if (strncmp("MSWIN", sect.oemname, 5) == 0) {
	    tsk_errno = TSK_ERR_MM_MAGIC;
	    snprintf(tsk_errstr, TSK_ERRSTR_L,
		"dos_load_prim_table: MSWIN OEM name exists");
	    tsk_errstr2[0] = '\0';
	    return 1;
	}
	else if (strncmp("NTFS", sect.oemname, 4) == 0) {
	    tsk_errno = TSK_ERR_MM_MAGIC;
	    snprintf(tsk_errstr, TSK_ERRSTR_L,
		"dos_load_prim_table: NTFS OEM name exists");
	    tsk_errstr2[0] = '\0';
	    return 1;
	}
    }

    /* Add an entry of 1 sector for the table  to the internal structure */
    if ((table_str = mymalloc(32)) == NULL)
	return 1;

    snprintf(table_str, 32, "Primary Table (#0)");
    if (NULL == mm_part_add(mm, DOS_PART_SOFFSET, (DADDR_T) 1,
	    MM_TYPE_DESC, table_str, -1, -1))
	return 1;

    /* Cycle through the partition table */
    for (i = 0; i < 4; i++) {
	dos_part *part = &sect.ptable[i];

	/* We currently ignore CHS */
	uint32_t part_start = getu32(mm, part->start_sec);
	uint32_t part_size = getu32(mm, part->size_sec);

	if (verbose)
	    fprintf(stderr,
		"load_pri:0:%d    Start: %" PRIu32 "   Size: %" PRIu32
		"  Type: %d\n", i, part_start, part_size, part->ptype);

	if (part_size == 0)
	    continue;

	if (part_start > max_addr) {
	    tsk_errno = TSK_ERR_MM_BLK_NUM;
	    snprintf(tsk_errstr, TSK_ERRSTR_L,
		"dos_load_prim_table: Starting sector too large for image");
	    tsk_errstr2[0] = '\0';
	    return 1;
	}
#if 0
// I'm not sure if this is too strict ...
	else if ((part_start + part_size) > max_addr) {
	    tsk_errno = TSK_ERR_MM_BLK_NUM
		snprintf(tsk_errstr, TSK_ERRSTR_L,
		"dos_load_prim_table: Partition ends after image");
	    tsk_errstr2[0] = '\0';
	    return 1;
	}
#endif

	/* Add the partition to the internal structure 
	 * If it is an extended partition, process it now */
	if (dos_is_ext(part->ptype)) {
	    if (NULL == mm_part_add(mm, (DADDR_T) part_start,
		    (DADDR_T) part_size, MM_TYPE_DESC,
		    dos_get_desc(part->ptype), 0, i))
		return 1;

	    if (dos_load_ext_table(mm, part_start, part_start, 1))
		return 1;
	}
	else {
	    if (NULL == mm_part_add(mm, (DADDR_T) part_start,
		    (DADDR_T) part_size, MM_TYPE_VOL,
		    dos_get_desc(part->ptype), 0, i))
		return 1;
	}
    }
    return 0;
}


/* 
 * Walk the partitions that have already been loaded during _open
 *
 * Return 1 on error and 0 on success
 */
uint8_t
dos_part_walk(MM_INFO * mm, PNUM_T start, PNUM_T last, int flags,
    MM_PART_WALK_FN action, char *ptr)
{
    MM_PART *part;
    PNUM_T cnt = 0;

    if (start < mm->first_part || start > mm->last_part) {
	tsk_errno = TSK_ERR_MM_WALK_RNG;
	snprintf(tsk_errstr, TSK_ERRSTR_L,
	    "dos_part_walk: Starting partition: %" PRIuPNUM "", start);
	tsk_errstr2[0] = '\0';
	return 1;
    }
    if (last < mm->first_part || last > mm->last_part) {
	tsk_errno = TSK_ERR_MM_WALK_RNG;
	snprintf(tsk_errstr, TSK_ERRSTR_L,
	    "dos_part_walk: Ending partition: %" PRIuPNUM "", last);
	tsk_errstr2[0] = '\0';
	return 1;
    }

    /* Cycle through the partitions */
    part = mm->part_list;
    while ((part != NULL) && (cnt <= last)) {

	/* Can we do the action yet? */
	if (cnt >= start) {
	    int retval;
	    retval = action(mm, cnt, part, 0, ptr);
	    if (retval == WALK_STOP)
		return 0;
	    else if (retval == WALK_ERROR)
		return 1;
	}

	part = part->next;
	cnt++;
    }

    return 0;
}


void
dos_close(MM_INFO * mm)
{
    mm_part_free(mm);
    free(mm);
}


/* 
 * Given the path to the file, open it and load the internal
 * partition table structure
 *
 * offset is the byte offset to the start of the volume system
 *
 * If test is 1 then additional tests are performed to make sure 
 * it isn't a FAT or NTFS file system
 */
MM_INFO *
dos_open(IMG_INFO * img_info, DADDR_T offset, uint8_t test)
{
    MM_INFO *mm = (MM_INFO *) mymalloc(sizeof(*mm));
    if (mm == NULL)
	return NULL;

    mm->mmtype = MM_DOS;
    mm->str_type = "DOS Partition Table";
    mm->img_info = img_info;

    mm->offset = offset;

    /* inititialize settings */
    mm->part_list = NULL;
    mm->first_part = mm->last_part = 0;
    mm->endian = 0;
    mm->dev_bsize = 512;
    mm->block_size = 512;


    /* Assign functions */
    mm->part_walk = dos_part_walk;
    mm->close = dos_close;

    /* Load the partitions into the sorted list */
    if (dos_load_prim_table(mm, test)) {
	dos_close(mm);
	return NULL;
    }

    /* fill in the sorted list with the 'unknown' values */
    if (mm_part_unused(mm)) {
	dos_close(mm);
	return NULL;
    }

    return mm;
}
